﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FlowBasis.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Reflection;

namespace FlowBasis.EntityFramework
{
    public class EntityFrameworkDataService : IDataService, IDisposable
    {
        private static MethodInfo s_genericFindMethodInfo;

        static EntityFrameworkDataService()
        {
            s_genericFindMethodInfo = typeof(EntityFrameworkDataService).GetMethods()
                .Where(m => m.Name == "Find" && m.IsGenericMethod).FirstOrDefault();
        }

        private DbContext dbContext;
        private IEntityPrimaryKeyService primaryKeyService;

        public EntityFrameworkDataService(DbContext dbContext, IEntityPrimaryKeyService primaryKeyService)
        {
            this.dbContext = dbContext;
            this.primaryKeyService = primaryKeyService;
        }

        public void Dispose()
        {
            if (this.dbContext != null)
            {
                this.dbContext.Dispose();
                this.dbContext = null;
            }
        }
        
        public IQueryable GetAll(Type objectType)
        {
            return this.dbContext.Set(objectType).AsNoTracking();
        }

        public IQueryable<T> GetAll<T>() where T : class
        {
            return this.dbContext.Set<T>().AsNoTracking();
        }

        public object Find(Type objectType, params object[] keyValues)
        {
            // NOTE: We cannot use Set().Find() because it will attach the objects to the context.

            var find = s_genericFindMethodInfo.MakeGenericMethod(objectType);
            return find.Invoke(this, new object[] { keyValues });
        }

        public T Find<T>(params object[] keyValues) where T : class
        {
            // NOTE: We cannot use Set<T>().Find() because it will attach the objects to the context.

            IList<string> primaryKeys = this.primaryKeyService.TryGetPrimaryKeyPropertyNames(typeof(T));
            var matchExpression = LinqExpressionHelper.CreateMultiplePropertyMatchLambdaExpression<T>(primaryKeys, keyValues);

            T result = this.GetAll<T>().SingleOrDefault(matchExpression);
            return result;
        }
       
    }
}
